#include "../include/Character.hh"
#include <iostream>
#include <iomanip>

Character::Character(const std::string& name, const std::string& desc)
    : name(name), description(desc), maxHealth(0), passiveAttackMultiplier(1.0f), defeatedEnemies(0) {
    attributes = AttributeBonuses();
    maxHealth = attributes.health;
    if (name == "Berserker") {
    } else if (name == "Mage") {
        fatalBlockAvailable = true;
    } else if (name == "Assassin") {
        controlImmune = true;
    }
}

Character::Character(Character&& other) noexcept
    : name(std::move(other.name)),
      description(std::move(other.description)),
      attributes(std::move(other.attributes)),
      maxHealth(other.maxHealth),
      skills(std::move(other.skills)),
      talents(std::move(other.talents)),
      isStunned(other.isStunned),
      weapon(std::move(other.weapon)),
      helmet(std::move(other.helmet)),
      armor(std::move(other.armor)),
      leggings(std::move(other.leggings)),
      boots(std::move(other.boots)),
      accessory(std::move(other.accessory)),
      attributeBonuses(std::move(other.attributeBonuses)),
      fatalBlockAvailable(other.fatalBlockAvailable),
      passiveAttackMultiplier(other.passiveAttackMultiplier),
      controlImmune(other.controlImmune),
      defeatedEnemies(other.defeatedEnemies) {
    other.isStunned = false;
    other.fatalBlockAvailable = false;
    other.passiveAttackMultiplier = 1.0f;
    other.controlImmune = false;
    other.maxHealth = 0;
    other.defeatedEnemies = 0;
}

Character& Character::operator=(Character&& other) noexcept {
    if (this != &other) {
        name = std::move(other.name);
        description = std::move(other.description);
        attributes = std::move(other.attributes);
        maxHealth = other.maxHealth;
        skills = std::move(other.skills);
        talents = std::move(other.talents);
        isStunned = other.isStunned;
        weapon = std::move(other.weapon);
        helmet = std::move(other.helmet);
        armor = std::move(other.armor);
        leggings = std::move(other.leggings);
        boots = std::move(other.boots);
        accessory = std::move(other.accessory);
        attributeBonuses = std::move(other.attributeBonuses);
        fatalBlockAvailable = other.fatalBlockAvailable;
        passiveAttackMultiplier = other.passiveAttackMultiplier;
        controlImmune = other.controlImmune;
        defeatedEnemies = other.defeatedEnemies;
        other.isStunned = false;
        other.fatalBlockAvailable = false;
        other.passiveAttackMultiplier = 1.0f;
        other.controlImmune = false;
        other.maxHealth = 0;
        other.defeatedEnemies = 0;
    }
    return *this;
}

void Character::displayInfo() const {
    std::cout << "Character: " << name << "\n";
    std::cout << "Description: " << description << "\n";
    std::cout << "Attributes:\n";
    std::cout << "  Health: " << attributes.health << "/" << maxHealth;
    if (attributeBonuses.count("health") && !attributeBonuses.at("health").empty()) {
        std::cout << " (";
        bool first = true;
        for (const auto& bonus : attributeBonuses.at("health")) {
            if (!bonus.isTemporary && bonus.source != "Heal" && bonus.source != "Health Potion" && bonus.source != "Blood Attack") {
                if (!first) std::cout << ", ";
                std::cout << "+" << bonus.value << " " << bonus.source;
                first = false;
            }
        }
        std::cout << ")";
    }
    std::cout << "\n";

    std::cout << "  Attack: " << attributes.attack;
    if (attributeBonuses.count("attack") && !attributeBonuses.at("attack").empty()) {
        std::cout << " (";
        bool first = true;
        for (const auto& bonus : attributeBonuses.at("attack")) {
            if (!bonus.isTemporary && bonus.source != "Heal") {
                if (!first) std::cout << ", ";
                std::cout << "+" << bonus.value << " " << bonus.source;
                first = false;
            }
        }
        std::cout << ")";
    }
    if (name == "Berserker" && defeatedEnemies > 0) {
        std::cout << " (Passive: +" << defeatedEnemies * 5 << ".0%)";
    }
    std::cout << "\n";

    std::cout << "  Defense: " << attributes.defense;
    if (attributeBonuses.count("defense") && !attributeBonuses.at("defense").empty()) {
        std::cout << " (";
        bool first = true;
        for (const auto& bonus : attributeBonuses.at("defense")) {
            if (!bonus.isTemporary && bonus.source != "Heal" && bonus.source != "Iron Shield" && bonus.source != "Golden Slash" && bonus.source != "Hellfire Storm" && bonus.source != "Shadow Burst") {
                if (!first) std::cout << ", ";
                std::cout << "+" << bonus.value << " " << bonus.source;
                first = false;
            }
        }
        std::cout << ")";
    }
    std::cout << "\n";

    std::cout << "  Speed: " << attributes.speed;
    if (attributeBonuses.count("speed") && !attributeBonuses.at("speed").empty()) {
        std::cout << " (";
        bool first = true;
        for (const auto& bonus : attributeBonuses.at("speed")) {
            if (!bonus.isTemporary && bonus.source != "Overload Pulse") {
                if (!first) std::cout << ", ";
                std::cout << "+" << bonus.value << " " << bonus.source;
                first = false;
            }
        }
        std::cout << ")";
    }
    std::cout << "\n";

    std::cout << "  Crit Rate: " << attributes.critChance << "%";
    if (attributeBonuses.count("critChance") && !attributeBonuses.at("critChance").empty()) {
        std::cout << " (";
        bool first = true;
        for (const auto& bonus : attributeBonuses.at("critChance")) {
            if (!bonus.isTemporary && bonus.source != "Golden Slash") {
                if (!first) std::cout << ", ";
                std::cout << "+" << bonus.value << " " << bonus.source;
                first = false;
            }
        }
        std::cout << ")";
    }
    std::cout << "\n";

    std::cout << "  Crit Damage: " << attributes.critDamage << "%";
    if (attributeBonuses.count("critDamage") && !attributeBonuses.at("critDamage").empty()) {
        std::cout << " (";
        bool first = true;
        for (const auto& bonus : attributeBonuses.at("critDamage")) {
            if (!bonus.isTemporary) {
                if (!first) std::cout << ", ";
                std::cout << "+" << bonus.value << " " << bonus.source;
                first = false;
            }
        }
        std::cout << ")";
    }
    std::cout << "\n";

    std::cout << "  Armor: " << attributes.armor;
    if (attributeBonuses.count("armor") && !attributeBonuses.at("armor").empty()) {
        std::cout << " (";
        bool first = true;
        for (const auto& bonus : attributeBonuses.at("armor")) {
            if (!bonus.isTemporary && bonus.source != "Juice Splash") {
                if (!first) std::cout << ", ";
                std::cout << "+" << bonus.value << " " << bonus.source;
                first = false;
            }
        }
        std::cout << ")";
    }
    std::cout << "\n";

    std::cout << "  Penetration: " << attributes.penetration;
    if (attributeBonuses.count("penetration") && !attributeBonuses.at("penetration").empty()) {
        std::cout << " (";
        bool first = true;
        for (const auto& bonus : attributeBonuses.at("penetration")) {
            if (!bonus.isTemporary) {
                if (!first) std::cout << ", ";
                std::cout << "+" << bonus.value << " " << bonus.source;
                first = false;
            }
        }
        std::cout << ")";
    }
    std::cout << "\n";

    std::cout << "  Damage Multiplier: " << attributes.damageMultiplier << "x";
    if (attributeBonuses.count("damageMultiplier") && !attributeBonuses.at("damageMultiplier").empty()) {
        std::cout << " (";
        bool first = true;
        for (const auto& bonus : attributeBonuses.at("damageMultiplier")) {
            if (!bonus.isTemporary) {
                if (!first) std::cout << ", ";
                std::cout << "+" << bonus.value << " " << bonus.source;
                first = false;
            }
        }
        std::cout << ")";
    }
    std::cout << "\n";

    std::cout << "Passive Skills:\n";
    if (name == "Berserker") {
        std::cout << "  - Berserker's Rage: Increases attack power by 5% for each enemy defeated (Current: " << defeatedEnemies * 5 << ".0%)\n";
    } else if (name == "Mage") {
        std::cout << "  - Magic Shield: Can block one fatal attack, restoring 1 health (Available: " << (fatalBlockAvailable ? "Yes" : "No") << ")\n";
    } else if (name == "Assassin") {
        std::cout << "  - Cloak of Shadows: Always acts first in combat\n";
    }

    std::cout << "Equipment:\n";
    std::cout << "  - Weapon: " << (weapon ? weapon->name + " (Level " + std::to_string(weapon->getLevel()) + ")" : "Basic Weapon") << "\n";
    std::cout << "  - Helmet: " << (helmet ? helmet->name + " (Level " + std::to_string(helmet->getLevel()) + ")" : "Basic Helmet") << "\n";
    std::cout << "  - Armor: " << (armor ? armor->name + " (Level " + std::to_string(armor->getLevel()) + ")" : "Basic Armor") << "\n";
    std::cout << "  - Leggings: " << (leggings ? leggings->name + " (Level " + std::to_string(leggings->getLevel()) + ")" : "Basic Leggings") << "\n";
    std::cout << "  - Boots: " << (boots ? boots->name + " (Level " + std::to_string(boots->getLevel()) + ")" : "Basic Boots") << "\n";
    std::cout << "  - Accessory: " << (accessory ? accessory->name + " (Level " + std::to_string(accessory->getLevel()) + ")" : "No Accessory") << "\n";

    if (!skills.empty()) {
        std::cout << "Skills:\n";
        for (const auto& skill : skills) {
            std::cout << "  - " << skill.name << ": " << skill.description << "\n";
        }
    }

    if (!talents.empty()) {
        std::cout << "Talents:\n";
        for (const auto& talent : talents) {
            std::cout << "  - " << talent.name << ": " << talent.description << "\n";
        }
    }
}

void Character::addSkill(const Skill& skill) {
    skills.push_back(skill);
}

bool Character::addEquipment(const Equipment& equip) {
    std::unique_ptr<Equipment>* slot = nullptr;
    std::string slotName;

    switch (equip.getType()) {
        case EquipmentType::WEAPON:
            slot = &weapon;
            slotName = "Weapon";
            break;
        case EquipmentType::HELMET:
            slot = &helmet;
            slotName = "Helmet";
            break;
        case EquipmentType::ARMOR:
            slot = &armor;
            slotName = "Armor";
            break;
        case EquipmentType::LEGGINGS:
            slot = &leggings;
            slotName = "Leggings";
            break;
        case EquipmentType::BOOTS:
            slot = &boots;
            slotName = "Boots";
            break;
        case EquipmentType::ACCESSORY:
            slot = &accessory;
            slotName = "Accessory";
            break;
        default:
            std::cout << "Invalid equipment type!" << std::endl;
            return false;
    }

    if (!slot) {
        std::cout << "Invalid equipment type!" << std::endl;
        return false;
    }

    AttributeBonuses oldAttributes = attributes;
    int oldMaxHealth = maxHealth;

    if (*slot) {
        if ((*slot)->getLevel() < 3) {
            (*slot)->upgrade();
            applyEquipmentBonuses(**slot);
            std::cout << slotName << " has been upgraded to level " << (*slot)->getLevel() << "!" << std::endl;

            if (attributes.attack > oldAttributes.attack) {
                attributeBonuses["attack"].push_back({equip.name, attributes.attack - oldAttributes.attack});
            }
            if (attributes.defense > oldAttributes.defense) {
                attributeBonuses["defense"].push_back({equip.name, attributes.defense - oldAttributes.defense});
            }
            if (attributes.speed > oldAttributes.speed) {
                attributeBonuses["speed"].push_back({equip.name, attributes.speed - oldAttributes.speed});
            }
            if (attributes.critChance > oldAttributes.critChance) {
                attributeBonuses["critChance"].push_back({equip.name, attributes.critChance - oldAttributes.critChance});
            }
            if (attributes.critDamage > oldAttributes.critDamage) {
                attributeBonuses["critDamage"].push_back({equip.name, attributes.critDamage - oldAttributes.critDamage});
            }
            if (attributes.armor > oldAttributes.armor) {
                attributeBonuses["armor"].push_back({equip.name, attributes.armor - oldAttributes.armor});
            }
            if (attributes.penetration > oldAttributes.penetration) {
                attributeBonuses["penetration"].push_back({equip.name, attributes.penetration - oldAttributes.penetration});
            }
            if (attributes.damageMultiplier > oldAttributes.damageMultiplier) {
                attributeBonuses["damageMultiplier"].push_back({equip.name, static_cast<int>((attributes.damageMultiplier - oldAttributes.damageMultiplier) * 100)});
            }
            if (maxHealth > oldMaxHealth) {
                attributeBonuses["health"].push_back({equip.name, maxHealth - oldMaxHealth});
            }

            return true;
        } else {
            std::cout << slotName << " has reached maximum level and cannot be upgraded further!" << std::endl;
            return false;
        }
    } else {
        *slot = std::make_unique<Equipment>(equip);
        applyEquipmentBonuses(**slot);
        std::cout << "Equipped new " << slotName << ": " << equip.name << " (Level 1)!" << std::endl;

        if (attributes.attack > oldAttributes.attack) {
            attributeBonuses["attack"].push_back({equip.name, attributes.attack - oldAttributes.attack});
        }
        if (attributes.defense > oldAttributes.defense) {
            attributeBonuses["defense"].push_back({equip.name, attributes.defense - oldAttributes.defense});
        }
        if (attributes.speed > oldAttributes.speed) {
            attributeBonuses["speed"].push_back({equip.name, attributes.speed - oldAttributes.speed});
        }
        if (attributes.critChance > oldAttributes.critChance) {
            attributeBonuses["critChance"].push_back({equip.name, attributes.critChance - oldAttributes.critChance});
        }
        if (attributes.critDamage > oldAttributes.critDamage) {
            attributeBonuses["critDamage"].push_back({equip.name, attributes.critDamage - oldAttributes.critDamage});
        }
        if (attributes.armor > oldAttributes.armor) {
            attributeBonuses["armor"].push_back({equip.name, attributes.armor - oldAttributes.armor});
        }
        if (attributes.penetration > oldAttributes.penetration) {
            attributeBonuses["penetration"].push_back({equip.name, attributes.penetration - oldAttributes.penetration});
        }
        if (attributes.damageMultiplier > oldAttributes.damageMultiplier) {
            attributeBonuses["damageMultiplier"].push_back({equip.name, static_cast<int>((attributes.damageMultiplier - oldAttributes.damageMultiplier) * 100)});
        }
        if (maxHealth > oldMaxHealth) {
            attributeBonuses["health"].push_back({equip.name, maxHealth - oldMaxHealth});
        }

        return true;
    }
}

void Character::applyEquipmentBonuses(const Equipment& equip) {
    equip.applyBonuses(*this);
}

void Character::addTalent(const Talent& talent, int layer) {
    AttributeBonuses oldAttributes = attributes;
    int oldMaxHealth = maxHealth;

    talents.push_back(talent);
    talent.applyBonuses(*this, layer);

    if (maxHealth > oldMaxHealth) {
        attributeBonuses["health"].push_back({talent.name, maxHealth - oldMaxHealth});
    }
    if (attributes.attack > oldAttributes.attack) {
        attributeBonuses["attack"].push_back({talent.name, attributes.attack - oldAttributes.attack});
    }
    if (attributes.defense > oldAttributes.defense) {
        attributeBonuses["defense"].push_back({talent.name, attributes.defense - oldAttributes.defense});
    }
    if (attributes.speed > oldAttributes.speed) {
        attributeBonuses["speed"].push_back({talent.name, attributes.speed - oldAttributes.speed});
    }
    if (attributes.critChance > oldAttributes.critChance) {
        attributeBonuses["critChance"].push_back({talent.name, attributes.critChance - oldAttributes.critChance});
    }
    if (attributes.critDamage > oldAttributes.critDamage) {
        attributeBonuses["critDamage"].push_back({talent.name, attributes.critDamage - oldAttributes.critDamage});
    }
    if (attributes.armor > oldAttributes.armor) {
        attributeBonuses["armor"].push_back({talent.name, attributes.armor - oldAttributes.armor});
    }
    if (attributes.penetration > oldAttributes.penetration) {
        attributeBonuses["penetration"].push_back({talent.name, attributes.penetration - oldAttributes.penetration});
    }
    if (attributes.damageMultiplier > oldAttributes.damageMultiplier) {
        attributeBonuses["damageMultiplier"].push_back({talent.name, static_cast<int>((attributes.damageMultiplier - oldAttributes.damageMultiplier) * 100)});
    }
}

void Character::onEnemyDefeated() {
    if (name == "Berserker") {
        defeatedEnemies++;
        float previousMultiplier = passiveAttackMultiplier;
        passiveAttackMultiplier = 1.0f + defeatedEnemies * 0.05f;
        int newAttack = static_cast<int>(attributes.attack / previousMultiplier * passiveAttackMultiplier);
        int attackIncrease = newAttack - attributes.attack;
        attributes.attack = newAttack;
        attributeBonuses["attack"].push_back({"Berserker's Rage", attackIncrease});
        std::cout << name << " defeated an enemy, triggering Berserker's Rage, attack power increased by 5%! Current attack: " << attributes.attack << "\n";
    }
}

bool Character::handleFatalDamage() {
    if (name == "Mage" && fatalBlockAvailable) {
        fatalBlockAvailable = false;
        attributes.health = 1;
        if (attributes.health > maxHealth) attributes.health = maxHealth;
        std::cout << name << " triggered Magic Shield, blocking fatal attack, health restored to 1!\n";
        return true;
    }
    return false;
}

void Character::heal(int amount) {
    attributes.health = std::min(attributes.health + amount, maxHealth);
}

const std::map<std::string, std::vector<Bonus>>& Character::getAttributeBonuses() const {
    return attributeBonuses;
}